library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.processor_types.all;

architecture algorithm of processor is
begin
    process
        constant allzero: std_logic_vector(31 downto 0) := (others => '0');
        variable pc, newpc : natural;
        variable current_instr : bit32;
            alias op    : bit6 is current_instr(31 downto 26);
            alias func  : bit6 is current_instr(5 downto 0);
            alias rs    : bit5 is current_instr(25 downto 21);
            alias rt    : bit5 is current_instr(20 downto 16);
            alias rd    : bit5 is current_instr(15 downto 11);
            alias rh    : bit5 is current_instr(10 downto 6);
            
            alias address : bit26 is current_instr(25 downto 0);
            alias imm     : bit16 is current_instr(15 downto 0);
        
        variable rs_int, rt_int, rd_int, rh_int : integer;
        variable address_int : integer;
        variable imm_int : integer;
        variable val_int : integer;
        variable val_long : signed(63 downto 0);
		variable val_shift: std_logic_vector(31 downto 0);
		variable booth_b: std_logic_vector(64 downto 0);
		variable booth_b_neg: std_logic_vector(64 downto 0);
		variable booth_tmp: std_logic_vector(32 downto 0);
		variable val_booth: std_logic_vector(64 downto 0);
		variable val_y_neg: std_logic_vector(32 downto 0);
		variable val_sltu_res: std_logic_vector(32 downto 0);
		variable val_loop: std_logic_vector(1 downto 0);
        
        variable registers : registers32;
        variable reg_hi, reg_lo : bit32;
        
        procedure memory_read(addr : in natural; result : out bit32) is
        begin
            a_bus <= std_logic_vector(to_unsigned(addr, 32));
            wait until clock = '1';
            if reset = '1' then
                return;
            end if;
            
            loop
                if reset = '1' then
                    return;
                end if;
                exit when ready = '0';
                wait until clock = '1';
            end loop;
            
            read <= '1' after 5 ns;
            wait until clock = '1';
            if reset = '1' then
                return;
            end if;
            
            loop
                wait until clock = '1';
                if reset = '1' then
                    return;
                end if;
                if ready = '1' then
                    result := d_busin;
                    exit;
                end if;
            end loop;
            
            wait until clock = '1';
            if reset = '1' then
                return;
            end if;
            
            read <= '0' after 5 ns;
            a_bus <= dontcare after 5 ns;
        end memory_read;
        
        procedure memory_write(addr : in natural; data : in bit32) is
        begin
            a_bus <= std_logic_vector(to_unsigned(addr, 32));
            wait until clock = '1';
            if reset = '1' then
                return;
            end if;
            
            loop
                if reset = '1' then
                    return;
                end if;
                exit when ready = '0';
                wait until clock = '1';
            end loop;
            
            d_busout <= data;
            wait until clock = '1';
            if reset = '1' then
                return;
            end if;
            write <= '1';
            
            loop
                wait until clock = '1';
                if reset = '1' then
                    return;
                end if;
                exit when ready = '1';
            end loop;
            
            wait until clock = '1';
            if reset = '1' then
                return;
            end if;
            
            write <= '0';
            a_bus <= dontcare;
            d_busout <= dontcare;
        end memory_write;
    begin

        if reset='1' then
            read <= '0';
            write <= '0';
            pc := 0;
            done <= '0';
            reg_hi := (others => '0');
            reg_lo := (others => '0');
            res <= allzero;
            for i in 0 to 31 loop
                registers(i) := (others => '0');
            end loop;
            loop
                wait until clock='1';
                exit when reset='0';
            end loop;
        else        
            done <= '1';
            wait until clock='1';
        
        
        done <= '0';
        memory_read(pc, current_instr);
        if reset /= '1' then
            newpc := pc + 4;
            case op is
                when rtype  =>  rs_int := to_integer(unsigned(rs));
                                rt_int := to_integer(unsigned(rt));
                                rd_int := to_integer(unsigned(rd));
                                rh_int := to_integer(unsigned(rh));
                                
                                case func is
                                    when add    => registers(rd_int) := std_logic_vector(signed(registers(rs_int)) + signed(registers(rt_int)));
												   res <= registers(rd_int);
                                    when mfhi   => registers(rd_int) := reg_hi;
												   res <= registers(rd_int);
                                    when mflo   => registers(rd_int) := reg_lo;
												   res <= registers(rd_int);
                                    when mult   => booth_b := (others => '0');
													booth_b(64 downto 33) := registers(rt_int);
													--booth_tmp := ('0' & (std_logic_vector(signed(registers(rt_int))) XOR "11111111111111111111111111111111"));
													--booth_tmp := std_logic_vector(to_signed(to_integer(signed(booth_tmp)) + 1, 33));
													--booth_b_neg := (others => '0');
													--booth_b_neg(64 downto 32) := booth_tmp;
													val_booth := (others => '0');
													val_booth(32 downto 1) := registers(rt_int);
													booth_loop: for i in 31 downto 0 loop
														val_loop := val_booth(1 downto 0);
														case val_loop is
															when "01" =>
																val_booth := std_logic_vector(signed(val_booth) + signed(booth_b));
															when "10" =>
																val_booth := std_logic_vector(signed(val_booth) - signed(booth_b));
															when others =>
																val_booth := val_booth;
														end case;
														val_booth := val_booth(64) & val_booth(64 downto 1);
													end loop booth_loop;
													--val_long := signed(registers(rs_int)) * signed(registers(rt_int));
                                                    reg_hi := std_logic_vector(val_booth(64 downto 33));
													res <= (others => '-');
                                                    reg_lo := std_logic_vector(val_booth(32 downto 1));
													
                                    when f_sll  =>  val_shift := std_logic_vector(unsigned(registers(rt_int)));
													if rh(0)='1' then val_shift := (val_shift(30 downto 0) & '0'); end if;
													if rh(1)='1' then val_shift := (val_shift(29 downto 0) & "00"); end if;
													if rh(2)='1' then val_shift := (val_shift(27 downto 0) & "0000"); end if;
													if rh(3)='1' then val_shift := (val_shift(23 downto 0) & "00000000"); end if;
													if rh(4)='1' then val_shift := (val_shift(15 downto 0) & "0000000000000000"); end if;
													registers(rd_int) := val_shift;
													res <= registers(rd_int);
													-- registers(rd_int) := std_logic_vector(unsigned(registers(rt_int)) sll rh_int);
												    -- res <= registers(rd_int);
                                    when sltu   =>  --val_y_neg := ('0' & (std_logic_vector(signed(registers(rt_int))) XOR "11111111111111111111111111111111"));
													--val_y_neg := std_logic_vector(to_signed(to_integer(signed(val_y_neg)) + 1, 33));
													val_sltu_res := std_logic_vector(signed(registers(rs_int)(31) & (registers(rs_int))) - signed(registers(rt_int)));
													if val_sltu_res(32)='1' then
														registers(rd_int) := (0 => '1', others => '0');
														res <= registers(rd_int);
                                                    else
														registers(rd_int) := (others => '0');
														res <= registers(rd_int);
                                                    end if;
													-- if unsigned(registers(rs_int)) < unsigned(registers(rt_int)) then
                                                    --    registers(rd_int) := (0 => '1', others => '0');
													--	  res <= registers(rd_int);
                                                    --else
                                                    --   registers(rd_int) := (others => '0');
													--	res <= registers(rd_int);
                                                    --end if;
                                    when f_nor  => registers(rd_int) := std_logic_vector(unsigned(registers(rs_int)) nor unsigned(registers(rt_int)));
												   res <= registers(rd_int);
                                    when others => assert false report "illegal r-type function detected" severity warning;
                                end case;
                
                when blez   =>  rs_int := to_integer(unsigned(rs));
                                val_int := to_integer(signed(registers(rs_int)));
                                imm_int := to_integer(signed(imm) sll 2);
                                if val_int <= 0 then
                                    newpc := newpc + imm_int;
                                end if;
								res <= std_logic_vector(to_unsigned(newpc,32));
                
                when j      =>  newpc := to_integer((to_unsigned(newpc, 32) and X"f0000000") or ("000000" & (unsigned(address) sll 2)));
							    res <= std_logic_vector(to_unsigned(newpc,32));
                
                when lw     =>  rs_int := to_integer(unsigned(rs));
                                rt_int := to_integer(unsigned(rt));
                                imm_int := to_integer(signed(imm));
                                memory_read(to_integer(unsigned(registers(rs_int))) + imm_int, registers(rt_int));
                                res <= (others => '-');
                
                when sw     =>  rs_int := to_integer(unsigned(rs));
                                rt_int := to_integer(unsigned(rt));
                                imm_int := to_integer(signed(imm));
                                memory_write(to_integer(unsigned(registers(rs_int))) + imm_int, registers(rt_int));
                                res <= registers(rt_int);
                                
                when others => assert false report "illegal instruction detected" severity warning;
            end case;
            pc := newpc;
            done <= '1';
        end if;
        end if;
    end process;

end algorithm;